package com.ib.review.util;


import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;

public class AESSecurity {
    private static final int KEY_BIT = 128;

    private static final String TYPE = "AES";

    private static final String DEFAULT_MODE = "ECB";

    private static final String DEFAULT_PADDING = "PKCS5Padding";

    private static final String MOBILE_AES_KEY = "DEFAULT_PADDING";

    private static Key getKey(String key) {
        byte[] keys = key.getBytes();
        byte[] keyBTmp = new byte[KEY_BIT / 8];
        for (int i = 0; i < keys.length && i < keyBTmp.length; i++) {
            keyBTmp[i] = keys[i];
        }
        return new SecretKeySpec(keyBTmp, TYPE);
    }

    private static String getSecurityType(String mode, String padding) {
        return TYPE + "/" + mode + "/" + padding;
    }

    public static String encode(String str, String key, String mode,
                                String padding) throws Exception {
        Key secretKey = getKey(key);
        Cipher cipher;
        try {
            cipher = Cipher.getInstance(getSecurityType(mode, padding));
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] b = cipher.doFinal(str.getBytes());
            return byte2hex(b);
        } catch (Exception e) {
            throw new Exception("AES加密失败", e);
        }
    }

    public static String decode(String str, String key, String mode,
                                String padding) throws Exception {
        Key secretKey = getKey(key);
        Cipher cipher;
        try {
            byte[] b = hex2byte(str);
            cipher = Cipher.getInstance(getSecurityType(mode, padding));
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            return new String(cipher.doFinal(b), StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new Exception("AES解密失败", e);
        }
    }

    public static String encode(String str, String key)
            throws Exception {
        return encode(str, key, DEFAULT_MODE, DEFAULT_PADDING);
    }

    public static String decode(String str, String key)
            throws Exception {
        return decode(str, key, DEFAULT_MODE, DEFAULT_PADDING);
    }

    public static String decode(String str, String key, String mode,
                                String padding, String encodeType) throws Exception {
        Key secretKey = getKey(key);
        Cipher cipher;
        try {
            byte[] b = hex2byte(str, encodeType);
            cipher = Cipher.getInstance(getSecurityType(mode, padding));
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            return new String(cipher.doFinal(b));
        } catch (Exception e) {
            throw new Exception("AES解密失败", e);
        }
    }

    public static byte[] hex2byte(String src) {
        byte[] bytes = src.getBytes();
        int iLen = bytes.length;
        byte[] arrOut = new byte[iLen / 2];
        for (int i = 0; i < iLen; i = i + 2) {
            String strTmp = new String(bytes, i, 2);
            arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
        }
        return arrOut;
    }

    public static byte[] hex2byte(String src, String encodeType) throws UnsupportedEncodingException {
        byte[] bytes = src.getBytes(encodeType);
        int iLen = bytes.length;
        byte[] arrOut = new byte[iLen / 2];
        for (int i = 0; i < iLen; i = i + 2) {
            String strTmp = new String(bytes, i, 2);
            arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
        }
        return arrOut;
    }

    public static String byte2hex(byte[] src) {
        String tmp;
        int iLen = src.length;
        StringBuilder str = new StringBuilder(iLen * 2);
        for (byte b : src) {
            tmp = (Integer.toHexString(b & 0XFF));
            if (tmp.length() == 1) {
                str.append("0");
            }
            str.append(tmp);
        }
        return (str.toString()).toUpperCase();
    }

    /**
     * 解密
     *
     * @param mobileCipher
     * @return
     * @throws Exception
     */
    public static String decryptForMobile(String mobileCipher) throws Exception {
        AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, MOBILE_AES_KEY.getBytes());
        String plain = aes.decryptStr(mobileCipher);
        return plain;
    }

    /**
     * 加密
     *
     * @param mobilePlain
     * @return
     * @throws Exception
     */
    public static String encryptForMobile(String mobilePlain) throws Exception {
        AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, MOBILE_AES_KEY.getBytes());
        String cipher = aes.encryptBase64(mobilePlain);
        return cipher;
    }

    public static String encrypt(String data, String key, String iv) throws Exception {
        //"算法/模式/补码方式"
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        int blockSize = cipher.getBlockSize();

        byte[] dataBytes = data.getBytes();
        int plaintextLength = dataBytes.length;
        if (plaintextLength % blockSize != 0) {
            plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
        }

        byte[] plaintext = new byte[plaintextLength];
        System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

        SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
        IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

        cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
        byte[] encrypted = cipher.doFinal(plaintext);
        return Base64.getEncoder().encodeToString(encrypted);
    }

    public static String desEncrypt(String data, String key, String iv) throws Exception {
        byte[] encrypted1 = Base64.getDecoder().decode(data);

        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
        IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

        byte[] original = cipher.doFinal(encrypted1);
        return new String(original);
    }

    public static void main(String[] args) {
        try {
//            String aa="AAF9D1B1F4146473D714CE674032EB81167617BB3E40B755BFB4C447687993047C9C82CB8E398ED86E8ADE0BD82234C4841CBDE5149588691CD5B129C3620F4F3542893FACC5C8E7B3DB1A45E95971DD48BB574A10E6F2E29039E0FFA28B2BD7BB0EEA99A9057B205F78A6C78D4BC484EC546550C77450D84480C32A3EEC0FEEE1C86BCC47C3BBAD2C8F570EA43C8B4B";
//            String decryptParam = AESSecurity.decode(aa,"ffcs_mobileh5v1");
//            System.out.println(decryptParam);
            String s = AESSecurity.encrypt("xxxxxx", "dufy20170329java", "dufy20170329java");
            System.out.println(s);
            System.out.println(AESSecurity.desEncrypt(s, "dufy20170329java", "dufy20170329java"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}